package com.panda.utils;

import com.panda.common.ResourceHolder;
import com.panda.expand.BodyParser;
import com.panda.expand.Expression;
import com.panda.expand.ParamParser;
import com.panda.expand.Sugar;
import com.panda.git.PullUtils;
import com.panda.pojo.*;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//解析单元
public class ParseUtils {


    //头表达式是一个空白的表达式,占位
    private static Expression firstExpression = new Expression() {
        @Override
        public void doParse(String e, Context context) {

        }
    };


    static {

        firstExpression.next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {

                return ">>".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {

                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                if (currentElement.getExpect() == Element.BTP || currentElement.getExpect() == Element.BTB) {

                    //退出当前上下文,并在父上下文中添加添加

                    currentElement.setExpect(-1);

                    Element parent = currentElement.getParent();


                    currentElement.getAttributes().put("eid", currentElement.getId());

                    //添加声明
                    parent.appendTempText(currentElement.getId());


                    //添加文本
                    parent.appendTempText(e);

                    context.setCurrentElement(parent);


                } else {

                    Element lastSubElement = currentElement.getLastSubElement();

                    //不一定得是输入型标签
                    if (lastSubElement != null) {

                        lastSubElement.getAttributes().put("eid", lastSubElement.getId());

                        currentElement.appendTempText(lastSubElement.getId());

                    }

                    currentElement.appendTempText(e);

                }


            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {//在该组件之后

                //是否是组件
                return context.isComponent(s);
            }

            @Override
            public void doParse(String e, Context context) {

                Element currentElement = context.getCurrentElement();

                if (currentElement.getExpect() == Element.BTP || currentElement.getExpect() == Element.BTB) {

                    //跳回父作用域,由父作用域进行解析
                    context.setCurrentElement(currentElement.getParent());

                    firstExpression.parse(e, context);

                    return;

                }


                //整体大致与创建一个普通标签相似,只是这里会被声明为组件
                Component element = new Component(context, e);

                element.setComponentName(e);

                //组件一定有subBlock
                element.setSubBlocks(new ArrayList<>());

                currentElement.addSubBlock(element);

                if (currentElement.isAssigning()) {

                    currentElement.setAssigning(false);

                    Map<String, String> attributes = element.getAttributes();

                    attributes.put("eid", element.getId());

                    currentElement.appendTempText(element.getId()).appendTempText("\n\n");

                }

                //期望小括号
                element.setExpect(Element.BTP);

                //进入到作用域
                context.setCurrentElement(element);

                //    System.out.println("进入作用域:" + e);


            }
        }).next(new Expression() {

            @Override
            public boolean canParse(String s, Context context) {

                return s.startsWith("@");
            }

            @Override
            public void doParse(String e, Context context) {

                String s = e;

                String[] split = s.substring(1).split("/");

                if (split.length > 3) {

                    String group = split[0];
                    String root = split[1];
                    String version = split[2];

                    //重新处理
                    if (PullUtils.pull(context, group, root, version, null)) {

                        firstExpression.parse(e, context);
                    } else {
                        //放行
                        getNext().parse(e, context);
                    }


                } else {
                    //放行
                    getNext().parse(e, context);
                }

            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {

                return targets.contains(s) && context.getCurrentElement().getTlpn() == 0 && context.getCurrentElement().getTlbn() == 0 && !context.getCurrentElement().isInputArrt();
            }

            @Override
            public void doParse(String e, Context context) {

                //如果当前元素期待前大小括号 就回退到父作用域在父作用域中处理

                Element currentElement = context.getCurrentElement();

                if (currentElement.getExpect() == Element.BTP || currentElement.getExpect() == Element.BTB) {

                    //跳回父作用域,由父作用域进行解析
                    context.setCurrentElement(currentElement.getParent());

                    firstExpression.parse(e, context);

                    return;

                }


                Element element = new Element(context, e);

                if (!noBodyTags.contains(e)) {
                    element.setSubBlocks(new ArrayList<>());
                }


                currentElement.addSubBlock(element);

                if (currentElement.isAssigning()) {

                    currentElement.setAssigning(false);

                    Map<String, String> attributes = element.getAttributes();

                    //给新建的元素添加uid
                    attributes.put("eid", element.getId());

                    currentElement.appendTempText(element.getId()).appendTempText("\n\n");

                }

                //期望小括号
                element.setExpect(Element.BTP);

                //进入到作用域
                context.setCurrentElement(element);

                //    System.out.println("进入作用域:" + e);


            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {

                //return ".".equals(s);

                return s.contains(".");

            }

            @Override
            public void doParse(String e, Context context) {


                //前面
                String pre = e.substring(0, e.indexOf('.'));

                //处理前半部分内容
                if (!pre.equals("")) {
                    firstExpression.parse(pre, context);
                }


                //处理点表达式
                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                //如果没有期待输入临时文本就退出到父上下文
                if (currentElement.getExpect() != Element.BTTmp) {

                    Element parent = currentElement.getParent();

                    context.setCurrentElement(parent);

                    parent.setLastSubElement(currentElement);

                    firstExpression.parse(e, context);

                } else {

                    Element lastSubElement = currentElement.getLastSubElement();

                    if (lastSubElement != null) {


                        //给新建的元素添加uid
                        lastSubElement.getAttributes().put("eid", lastSubElement.getId());

                        currentElement.appendTempText(lastSubElement.getId()).appendTempText(".");

                    } else {
                        currentElement.appendTempText(".");
                    }


                }


                String post = e.substring(e.indexOf('.') + 1);

                //处理后半部分的内容
                if (!post.equals("")) {
                    firstExpression.parse(post, context);
                }

            }
        }).next(new Expression() {//处理赋值环境

            @Override
            public boolean canParse(String s, Context context) {
                return s.contains("=");
            }

            @Override
            public void doParse(String e, Context context) {

                Element currentElement = context.getCurrentElement();

                if (currentElement.getExpect() == Element.BTB || currentElement.getExpect() == Element.BTP) {

                    currentElement.setExpect(-1);

                    //跳转回复作用域重新解析这个句子
                    context.setCurrentElement(currentElement.getParent());
                    firstExpression.parse(e, context);

                    return;
                }


                int index = e.indexOf('=');

                String post = e.substring(index + 1);
                String pre = e.substring(0, index + 1);

                //可能是条件判断例如 ==,>=,<=等,如果是这种情形不能属于赋值环境
                if (post.startsWith("=") || pre.endsWith("<") || pre.endsWith(">")) {
                    currentElement.appendTempText(e);
                } else {


                    //为什么又将内容添加给了下一个元素
                    currentElement.appendTempText(pre);

                    //转变为赋值环境
                    currentElement.setAssigning(true);

                    if (!"".equals(post)) {
                        ParseUtils.parse(post, context);
                    }

                }


            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {


                //有字符串 左括号为空 非赋值状态
                return s.contains("__string_");
            }

            @Override
            public void doParse(String e, Context context) {

                Element currentElement = context.getCurrentElement();


                //如果期待前大括号或者小括号就跳回父上下文

                if (currentElement.getExpect() == Element.BTP || currentElement.getExpect() == Element.BTB) {
                    currentElement.setExpect(-1);
                    context.setCurrentElement(currentElement.getParent());
                    firstExpression.parse(e, context);
                    return;
                }

                //不处理
                if (currentElement.isAssigning() || currentElement.isInputArrt() || currentElement.getTlbn() != 0 || currentElement.getTlpn() != 0) {
                    getNext().parse(e, context);
                    return;
                }


/*

                currentElement.getExpect() == Element.BTTmp && currentElement.getTlbn() == 0 && currentElement.getTlpn() == 0 && !currentElement.isAssigning() && !currentElement.isInputArrt()
*/


                List<String> ss = StringUtils.transform2output(e);

                for (String s : ss) {
                    this.getNext().parse(s, context);
                }

            }

        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {

                Element currentElement = context.getCurrentElement();

                //在期望追加临时文本并且左括号数量为0时

                return "<<".equals(s) && currentElement.getExpect() == Element.BTTmp && currentElement.getTlbn() == 0 && currentElement.getTlpn() == 0;
            }

            @Override
            public void doParse(String e, Context context) {


                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                StringBlock stringBlock = new StringBlock();

                currentElement.appendTempText("<<" + stringBlock.getId());

                currentElement.addSubBlock(stringBlock);

            }
        })/*.next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {
                return "current".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {


                //追加为current元素
                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                currentElement.setLastSubElement(null);

                currentElement.getAttributes().put("eid", currentElement.getId());

                currentElement.appendTempText(currentElement.getId());

            }
        })*/.next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {

                return "there".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {

                //创建一个there标签插入到这里,并指明id

                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                currentElement.setLastSubElement(null);

                Element element = new Element(context, "there");

                element.setSubBlocks(Collections.emptyList());

                Map<String, String> attributes = element.getAttributes();

                attributes.put("eid", element.getId());

                currentElement.addSubBlock(element);

                currentElement.appendTempText(element.getId());


            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {
                return "(".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {


                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                //如果期望小括号才进入小括号
                if (currentElement.getExpect() == Element.BTP) {


                    //   System.out.println("进入左小括号:" + context.getCurrentElement().getName());

                    //期望临时文本
                    currentElement.setExpect(Element.BTTmp);

                    currentElement.setInputArrt(true);

                } else {
                    currentElement.appendTempText(e);
                    currentElement.setTlpn(currentElement.getTlpn() + 1);
                }


            }
        }).next(new Expression() {

            @Override
            public boolean canParse(String s, Context context) {
                return ")".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {


                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                //左括号的数量
                int tlpn = currentElement.getTlpn();

                if (tlpn == 0) {

                    StringBuilder bufferedText = currentElement.getBufferedText();

                    //将文本写入参数体中
                    currentElement.setParamsText(bufferedText.toString().trim());

                    //清空内容
                    bufferedText.delete(0, bufferedText.length());

                    //    System.out.println("退出右小括号:" + currentElement.getName());

                    //期望大括号
                    currentElement.setExpect(Element.BTB);

                    currentElement.setInputArrt(false);

                    //不在这里退出作用域
/*
                    if (currentElement.getSubBlocks() == null) {

                        System.out.println("后括号回退作用域:" + currentElement.getName());

                        currentElement.setExpect(-1);

                        //回退到父作用域
                        context.setCurrentElement(currentElement.getParent());

                    } else {
                    }
*/

                } else {

                    currentElement.appendTempText(e);

                    currentElement.setTlpn(tlpn - 1);

                }


            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {
                return "{".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {


                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                //如果期望大括号,或者小括号
                if (currentElement.getExpect() == Element.BTB || currentElement.getExpect() == Element.BTP) {

                    //    System.out.println("进入左大括号:" + currentElement.getName());

                    //不接受标签体但是可以输入逻辑代码
                    /*
                    if (currentElement.getSubBlocks() == null) {
                        throw new RuntimeException(currentElement.getName() + " 标签不接受标签体");
                    }*/

                    currentElement.setExpect(Element.BTTmp);

                } else {
                    currentElement.appendTempText("\n").appendTempText(e).appendTempText("\n");
                    currentElement.setTlbn(currentElement.getTlbn() + 1);
                }


            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {

                return "}".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {

                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                if (currentElement.getExpect() != Element.BTTmp) {

                    context.setCurrentElement(currentElement.getParent());

                    firstExpression.parse(e, context);

                    //直接退出当前作用域
                    return;
                }


                //作用域内左大括号的数量
                int tlbn = currentElement.getTlbn();

                //当作用域内的左大括号数量减为0时
                if (tlbn == 0) {

                    StringBuilder bufferedText = currentElement.getBufferedText();
                    currentElement.setBodyText(bufferedText.toString().trim());
                    bufferedText.delete(0, bufferedText.length());

                    //    System.out.println("退出右大括号:" + currentElement.getName());

                    Element parent = currentElement.getParent();

                    //如果是右大括号一定时退回到了父作用域
                    if (parent != null) {


                        //退回到父作用域
                        context.setCurrentElement(parent);

                        //期望临时文本
                        parent.setExpect(Element.BTTmp);

                        parent.setLastSubElement(currentElement);

                    }

                    //退出输入临时文本


                    //不希望任何处理
                    currentElement.setExpect(-1);


                } else {

                    //前后换行
                    currentElement.appendTempText("\n").appendTempText(e).appendTempText("\n");
                    currentElement.setTlbn(tlbn - 1);
                }

            }
        }).next(new Expression() {
            @Override
            public boolean canParse(String s, Context context) {
                return "\n".equals(s);
            }

            @Override
            public void doParse(String e, Context context) {

                Element currentElement = context.getCurrentElement();
                currentElement.appendTempText(e);

            }
        }).next(new Expression() {//末端处理,到这里的文本将会追加到脚本文本中
            @Override
            public boolean canParse(String s, Context context) {
                return true;
            }

            @Override
            public void doParse(String e, Context context) {

                Element currentElement = context.getCurrentElement();

                currentElement.setAssigning(false);

                int expect = currentElement.getExpect();


                //正在输入临时文本

                //只有期望输入临时文本的时候才会输入临时文本
                if (expect == Element.BTTmp) {

                    currentElement.appendTempText(e);

                    currentElement.setLastSubElement(null);

                } else if (expect == Element.BTB || expect == Element.BTP) {

                    //并没有执行回退操作
                    currentElement.setExpect(-1);

                    Element parent = currentElement.getParent();

                    //退回到父上下文
                    context.setCurrentElement(parent);

                    parent.setExpect(Element.BTTmp);

                    parent.setLastSubElement(null);

                    parent.appendTempText(e);

                }

            }

        });

    }

    static {

        //初始化扩展
        ExpandInit.init();

    }

    public static void parse(Context context) {

        String contextText = context.getText();

        contextText = FragmentUtils.extract(context, contextText);

        String[] split = contextText.split("[\n]");

        for (String s : split) {

            s = s.trim();

            if (!"".equals(s)) {
                parse(s, context);
            }

        }


        Element element = context.getRootElement();

        deepParse(context, element);


    }

    public static void parse(Context context, ResourceHolder holder) {

        //提取字符串
        StringUtils.extract(context);

        //移除注释
        StringUtils.removeAnnotation(context);

        DocumentSlicer.extractFiles(context);

        ViewUtils.isView(context);

        parse(context);

        ProcessUtils.deepProcess(context, holder.getConfig());

    }


    /*

    某个元素可能会被重复处理,例如在创建一个动态元素,或者在定义一个组件时,因为创建了新的上下文,所以都会重复处理

     */
    private static void deepParse(Context context, Element element) {

        //先解析参数

        if (!element.isHasParsed()) {
            parseElementParams(context, element);
        }


        //如果是组件
        if (element instanceof Component) {

            //如果不是正在解析中的组件
            if (!((Component) element).getComponentName().equals(context.getParsingComponent())) {

                //处理映射
                handleMapping(context, (Component) element);

            } else {

                if (!element.isHasParsed()) {

                    ((Component) element).setCanMappingScopeParams(new HashSet<>());

                    parseElementBody(context, element);
                }
            }

        } else {

            if (!element.isHasParsed()) {
                parseElementBody(context, element);
            }

        }


        List<Block> subBlocks = element.getSubBlocks();


        if (subBlocks != null) {

            for (Block block : subBlocks) {
                if (block instanceof Element) {
                    deepParse(context, (Element) block);
                }
            }
        }

        element.setHasParsed(true);

    }

    private static void handleMapping(Context context, Component element) {

        Component component = context.getComponent(element.getComponentName());

        //由于使用了as,所以这里是别名
        if (component == null) {

            throw new RuntimeException("不存在的组件:" + element.getName());
        }


        element.setName(component.getName());

        Map<String, String> map = new HashMap<>();

        map.putAll(component.getAttributes());

        map.putAll(element.getAttributes());

        element.setCanMappingScopeParams(component.getCanMappingScopeParams());

        element.setAttributes(map);

        element.setJs(component.getJs());

        element.setCss(component.getCss());

        element.setParent(component.getParent());

        element.setSubBlocks(component.getSubBlocks());

        Map<String, String> localStringMap = component.getLocalStringMap();

        element.setLocalStringMap(localStringMap);

        Scope componentScope = component.getScope();

        String script = componentScope.getScript();

        Scope scope = new Scope();

        if (script != null) {
            scope.setScript(StringUtils.fillBack(localStringMap, script));
        }

        scope.setParams(componentScope.getParams());

        scope.setId(IdUtils.gCreate(context, "__clone_scope_"));

        scope.setDependencies(componentScope.getDependencies());

        scope.setElement(element);

        element.setScope(scope);

        MappingUtils.mapping(element);

        //映射处理之后 如果有脚本
        if (element.getScope().getScript() != null && !"".equals(element.getScope().getScript())) {

            element.getAttributes().put("ng-controller", element.getScope().getId());

        }

    }

    private static Set<String> targets;
    private static Set<String> noBodyTags;

    static void setTargets(Set<String> targets) {
        ParseUtils.targets = targets;
    }

    static void setNoBodyTags(Set<String> noBodyTags) {
        ParseUtils.noBodyTags = noBodyTags;
    }

    //添加解析表达式
    static void addExpression(Expression expression) {

        Expression next = firstExpression.getNext();

        firstExpression.next(expression).next(next);

    }

    //添加一个语法糖
    static void addSugar(Sugar sugar) {

        sugar.setFirstExpression(firstExpression);

        addExpression(sugar);

        addBodyParser(sugar);

        addParamParser(sugar);

    }


    private static void parseElementParams(Context context, Element currentElement) {

        String s = currentElement.getParamsText();

        if (paramParsers != null) {
            for (ParamParser paramParser : paramParsers) {
                s = paramParser.parseParam(context, currentElement, s);
            }
        }

        if (s == null || "".equals(s)) {
            return;
        }

        s = s.replaceAll("(?<=[^=])=(?=[^=])", " = ");

        List<String> list = new ArrayList<>(Arrays.asList(s.split(" +")));

        Map<String, String> rawAttrs = new HashMap<>();

        for (int i = 0; i < list.size(); i++) {

            if ("=".equals(list.get(i))) {

                String value = list.get(i + 1);

                list.remove(i + 1);

                list.remove(i);

                String key = list.get(i - 1);

                list.remove(i - 1);

                rawAttrs.put(key, value);

                //后退

                i--;

            }

        }

        Map<String, String> attributes = currentElement.getAttributes();

        list.forEach(key -> rawAttrs.put(key, null));


        Set<String> strings = context.getStringMap().keySet();


        //之后再用默认的方式进行解析
        rawAttrs.forEach((key, value) -> {

            if (value != null) {

                if (strings.contains(value)) {
                    attributes.put(key, value);
                } else {

                    if (key.startsWith("ng-")) {
                        attributes.put(key, value);
                    } else {
                        attributes.put("ng-attr-" + key, "{{" + value + "}}");
                    }
                }

            } else {
                attributes.put(key, null);
            }

        });

    }

    private static List<ParamParser> paramParsers;

    static void addParamParser(ParamParser paramParser) {

        if (paramParsers == null) {
            paramParsers = new ArrayList<>();
        }
        paramParsers.add(paramParser);

    }

    static interface AfterParseScript {
        void onAfterParseScript();
    }

    private static void parseElementBody(Context context, Element element) {


        String bodyText = element.getBodyText();

        if (bodyText == null || "".equals(bodyText)) {
            return;
        }


        String s = bodyText;

        s = s.trim();


        if (!"".equals(s)) {

            List<AfterParseScript> afterParseScripts = new ArrayList<>();

            element.setProtectedStrings(new LinkedHashSet<>());

            //处理输入符 >>
            s = InputUtils.transformInput(element, s, afterParseScripts);

            //处理输出符号 <<
            s = OutputUtils.transformOutput(element, s, afterParseScripts);

            //将函数声明变更为变量声明的形式
            s = FunctionUtils.transform(s);

            //提取变量
            s = ParamUtils.extractParam(context, element.getScope(), s);

            if (bodyParsers != null) {
                for (BodyParser bodyParser : bodyParsers) {
                    s = bodyParser.parseBody(context, element, s);
                }
            }

            //解析为标签元素
            s = DynamicElementUtils.transform2element(context, element, s);

            //标签引用,处理为$('[eid="xxxxx"]')的形式
            s = QuoteUtils.transformQuote(context, element, s);

            //处理插入符号 >>>
            s = InsertUtils.transform(context, element, s);

            //处理监听
            s = WatchUtils.transform(s);

            s = ServiceUtils.transformDep(context, element.getScope(), s);

            //设置脚本
            Scope scope = element.getScope();

            if (!"".equals(s.trim())) {

                scope.setScript(s.replaceAll("[\n]+", "\n"));

                if (!(element instanceof Component)) {
                    element.getAttributes().put("ng-controller", scope.getId());
                }


                //变量替换
                scope.setScript(ParamUtils.transform(context, element.getScope(), scope.getScript()));


                //函数中的形参
                scope.setScript(FunctionUtils.protectParam(scope.getScript()));


                //受保护字符串展开
                scope.setScript(StringUtils.fillProtected(element, scope.getScript()));

            }

            element.setProtectedStrings(null);


            for (AfterParseScript afterParseScript : afterParseScripts) {
                afterParseScript.onAfterParseScript();
            }


        }


    }

    private static List<BodyParser> bodyParsers;

    static void addBodyParser(BodyParser parser) {
        if (bodyParsers == null) {
            bodyParsers = new ArrayList<>();
        }
        bodyParsers.add(parser);

    }

    // "|(?<=[^=])?=(?=[^=])?"   //赋值符号

    private static Pattern sp1 = Pattern.compile(
            "(\\{)" +
                    "|(})" +            //前后大括号
                    "|(\\()" +
                    "|(\\))" +          //前后小括号
                    "|(?<=[^>])?>>>?(?=[^>])?" +    //输入或插入
                    "|(?<=[^<])?<<(?=[^<])?" +   //输出
                    //   "|\\." +            //点
                    "|;"
    );


    private static void parse(String sentence, Context context) {

        Matcher matcher = sp1.matcher(sentence);

        int end = 0;

        String t;

        while (matcher.find()) {

            t = sentence.substring(end, matcher.start()).trim();

            if (!"".equals(t)) {
                firstExpression.parse(t, context);
            }

            firstExpression.parse(matcher.group().trim(), context);

            end = matcher.end();

        }


        t = sentence.substring(end).trim();

        if (!"".equals(t)) {
            firstExpression.parse(t, context);
        }

        firstExpression.parse("\n", context);

    }

}

